﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Runtime.Serialization.Json;
using Wyse.Server.Logging;

namespace Wyse.Server.Base
{
    /// <summary>
    /// Thrown when the parser encounters an error
    /// </summary>
    public class MessageParseException : Exception { }

    /// <summary>
    /// Creates message objects from strings
    /// </summary>
    public class MessageParser : IMessageConsumer
    {
        /// <summary>
        /// Ctor
        /// </summary>
        /// <param name="processor"></param>
        public MessageParser(IMessageProcessor processor)
        {
            mProcessor = processor;
        }

        /// <summary>
        /// Parses the specified message
        /// </summary>
        /// <param name="message"></param>
        public void ConsumeMessage(byte[] message, int length)
        {
             Object data = null;
            String action = null;

            XmlDictionaryReader r = JsonReaderWriterFactory.CreateJsonReader(message, 0, length,
                XmlDictionaryReaderQuotas.Max);

            int state = 0;
            bool readParams = false;

            while (state != -1 && r.Read())
            {
                switch (state)
                {
                    case 0:
                        state = 1;
                        break;

                    case 1:
                        if (r.NodeType == XmlNodeType.Element)
                        {
                            if (r.Name == "action")
                            {
                                state = 2;
                            }
                            else if (r.Name == "params")
                            {
                                if (action == null)
                                    action = DiscoverActionName(message, length);

                                readParams = true;
                                state = -1;
                                break;
                            }
                            else
                                r.Skip();
                        }
                        break;

                    case 2:
                        if (r.NodeType != XmlNodeType.Text)
                            throw new MessageParseException();
                        action = r.Value;
                        state = 1;
                        break;
                }
            }

            if (action == null)
                throw new MessageParseException();

            if (readParams)
            {
                try
                {
                    var serializer = MessageTypeMgr.GetSerializer(action);
                    if (serializer != null)
                        data = serializer.ReadObject(r, false);
                }
                catch(Exception e)
                {
                    Log.Error("Failed to deserialize the parameters of message {0} ({1})", action, e.Message);
                }
            }

            mProcessor.ProcessMessage(new Message(action, data));
        }

        /// <summary>
        /// Does a second pass to extract the action name
        /// </summary>
        /// <param name="messageBytes"></param>
        /// <returns></returns>
        private String DiscoverActionName(byte[] messageBytes, int length)
        {
            XmlDictionaryReader r = JsonReaderWriterFactory.CreateJsonReader(messageBytes, 0, length,
                XmlDictionaryReaderQuotas.Max);

            int state = 0;

            while (r.Read())
            {
                switch (state)
                {
                    case 0:
                        state = 1;
                        break;

                    case 1:
                        if (r.NodeType == XmlNodeType.Element)
                        {
                            if (r.Name == "action")
                            {
                                state = 2;
                            }
                            else
                                r.Skip();
                        }
                        break;

                    case 2:
                        if (r.NodeType != XmlNodeType.Text)
                            throw new MessageParseException();
                        return r.Value;
                }
            }

            return null;
        }

        private IMessageProcessor mProcessor;
    }
}
